package com.itcs.app.controller;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.gson.GsonBuilder;
import com.itcs.app.util.JwtParamBuilder;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.compression.CompressionCodecs;
import io.swagger.annotations.Api;

/**
 * 电子发票相关
 * @author hanhengwang
 * @date 2018-04-16 10:35
 *
 */
@Api(value = "电子发票controller", tags = { "电子发票操作接口" })
@RestController
@RequestMapping("/invoice")
public class InvoiceControllerDemo {
	/** 发票APPID */
	private static final String INVOICE_APPID = "commontesterCA";
	/** 发票请求网关 */
	private static final String INVOICE_URL = "https://yesfp.yonyoucloud.com";
	/** 销售方纳税人识别号 */
	private static final String XSF_NSRSBH = "201609140000001";
	/** 公司代号 */
	private static final String ORG_CODE = "20160914001";
	/** 证书私钥路径 */
	private static final String KEY_PATH = "C:\\Users\\wone\\Desktop\\pro22.pfx";
	/** 证书密码 */
	private static final String KEY_PASSWORD = "password";

	public static void main(String[] args) {
	    try {
	      new InvoiceControllerDemo().callInvoiceApply();
	    } catch (Exception e) {
	      e.printStackTrace();
	    }
	}
	
	public void callInvoiceApply() throws Exception {
	    String url = INVOICE_URL + "/invoiceclient-web/api/invoiceApply/insertWithArray?"
	        + "appid=" + INVOICE_APPID;
	
	    HttpClient httpClient = HttpClients.custom().build();
	    HttpPost httpPost = new HttpPost(url);
	
	    // 构造POST表单Map
	    Map<String, String> paramsMap = buildPostParam();
	
	    // 签名
	    String sign = this.sign(paramsMap);
	    httpPost.addHeader("sign", sign);
	
	    // 转换POST表单参数
	    List<NameValuePair> list = new ArrayList<NameValuePair>();
	    Iterator<Entry<String, String>> iterator = paramsMap.entrySet().iterator();
	    while (iterator.hasNext()) {
	      Entry<String, String> elem = iterator.next();
	      list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
	    }
	    if (list.size() > 0) {
	      UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
	      httpPost.setEntity(entity);
	    }
	
	    // 发送http post请求，并得到响应结果
	    HttpResponse response = httpClient.execute(httpPost);
	    String result = "";
	    if (response != null) {
	      HttpEntity resEntity = response.getEntity();
	      if (resEntity != null) {
	        result = EntityUtils.toString(resEntity, "UTF-8");
	        System.out.println(result);
	      }
	    }
	  }
	
	  /**
	   * 获取发票请求流水号
	   * 
	   * @return 发票请求流水号
	   */
	 private String buildFpqqlsh() {
	    return "20180416"+System.currentTimeMillis() / 1000L;
	 }
	
	  /**
	   * 签名
	   * 
	   * @param paramsMap 表单参数
	   * @return 签名值
	   * @throws Exception
	   */
	 private String sign(Map<String, String> paramsMap) throws Exception {
	
	    // 读取CA证书与PEM格式证书需要根据实际证书使用情况而定,目前这两种都支持
	    PrivateKey privateKey = loadPrivateKeyOfCA();
	
	    Map<String, Object> claims =
	        JwtParamBuilder.build().setSubject("tester").setIssuer("einvoice").setAudience("einvoice")
	            .addJwtId().addIssuedAt().setExpirySeconds(3600).setNotBeforeSeconds(3600).getClaims();
	
	    // 需要将表单参数requestdatas的数据进行md5加密，然后放到签名数据的requestdatas中。
	    // 此签名数据必须存在，否则在验证签名时会不通过。
	    String value = paramsMap.get("requestdatas");
	    claims.put("requestdatas", getMD5(value));
	
	    // 使用jdk1.6版本时，删除下面代码中的.compressWith(CompressionCodecs.DEFLATE)
	    String compactJws = Jwts.builder().signWith(SignatureAlgorithm.RS512, privateKey)
	        .setClaims(claims).compressWith(CompressionCodecs.DEFLATE).compact();
	
	    return compactJws;
	 }
	
	  /**
	   * 计算MD5
	   * 
	   * @param str
	   * @return
	   * @throws UnsupportedEncodingException
	   * @throws NoSuchAlgorithmException
	   */
	 private String getMD5(String str) throws UnsupportedEncodingException, NoSuchAlgorithmException {
	    byte[] buf = null;
	    buf = str.getBytes("utf-8");
	    MessageDigest md5 = null;
	    md5 = MessageDigest.getInstance("MD5");
	    md5.update(buf);
	    byte[] tmp = md5.digest();
	    StringBuilder sb = new StringBuilder();
	    for (byte b : tmp) {
	      sb.append(String.format("%02x", b & 0xff));
	    }
	    return sb.toString();
	 }
	
	
	  /**
	   * 读取证书私钥
	   * 
	   * @return
	   * @throws UnrecoverableKeyException
	   * @throws KeyStoreException
	   * @throws NoSuchAlgorithmException
	   * @throws CertificateException
	   * @throws IOException
	   */
	 protected PrivateKey loadPrivateKeyOfCA() throws UnrecoverableKeyException, KeyStoreException,
	      NoSuchAlgorithmException, CertificateException, IOException {
	    FileInputStream in = new FileInputStream(KEY_PATH);
	    KeyStore ks = KeyStore.getInstance("pkcs12");
	    ks.load(in, KEY_PASSWORD.toCharArray());
	    String alias = ks.aliases().nextElement();
	    PrivateKey caprk = (PrivateKey) ks.getKey(alias, KEY_PASSWORD.toCharArray());
	    return caprk;
	 }
	
	  /**
	   * post表单数据
	   * 
	   * @return
	   */
	 private Map<String, String> buildPostParam() {
	    Map<String, String> paramsMap = new HashMap<String, String>();
	    paramsMap.put("requestdatas", this.buildRequestDatas());
	    paramsMap.put("email", this.buildEmailConfigs());
	    paramsMap.put("sms", this.buildSmsConfigs());
	    paramsMap.put("url", this.buildUrlConfigs());
	    paramsMap.put("autoAudit", "true");
	
	    return paramsMap;
	 }
	
	  /**
	   * url回掉配置
	   * 
	   * @return
	   */
	 private String buildUrlConfigs() {
	    List<Object> datas = new ArrayList<>();
	    Map<String, Object> data = new HashMap<>();
	    data.put("fpqqlsh", buildFpqqlsh());
	    data.put("url", "http://127.0.0.1:7787/EinvoiceRESTService/CallBackEInvoices/");
	    datas.add(data);
	
	    GsonBuilder builder = new GsonBuilder();
	    return builder.create().toJson(datas);
	 }
	
	  /**
	   * 构造短信发送信息
	   * 
	   * @return
	   */
	 private String buildSmsConfigs() {
	    List<Object> datas = new ArrayList<>();
	    Map<String, Object> data = new HashMap<>();
	    data.put("fpqqlsh", buildFpqqlsh());
	    data.put("address", "18811658522");
	    datas.add(data);
	
	    GsonBuilder builder = new GsonBuilder();
	    return builder.create().toJson(datas);
	 }
	
	  /**
	   * 构造email发送信息
	   * 
	   * @return
	   */
	 private String buildEmailConfigs() {
	    List<Object> datas = new ArrayList<>();
	    Map<String, Object> data = new HashMap<>();
	    data.put("fpqqlsh", buildFpqqlsh());
	    data.put("address", "shishigo88@outlook.com");
	    datas.add(data);
	
	    GsonBuilder builder = new GsonBuilder();
	    return builder.create().toJson(datas);
	 }
	
	  /**
	   * 构造requestdatas
	   * 
	   * @return
	   */
	 private String buildRequestDatas() {
	    List<Object> datas = new ArrayList<>();
	    Map<String, Object> data = new HashMap<>();
	    data.put("FPQQLSH", buildFpqqlsh());//发票流水号
	    data.put("XSF_NSRSBH", XSF_NSRSBH);//销售方纳税人识别号
	    data.put("ORGCODE", ORG_CODE);//
	    data.put("GMF_MC", "测试开票用户");//购买方名称
	    data.put("JSHJ", 117);//价税合计
	    
	    data.put("fpHm", "");//发票号码
	    data.put("fpDm", "");//发票代码
	    data.put("KPR", "");//开票人
	    data.put("SKR", "");//收款人
	    data.put("FHR", "");//复核人
	    data.put("HJJE", "");//合计金额
	    data.put("HJSE", "");//合计税额
	    
	    data.put("items", buildItems());
	    datas.add(data);
	
	    GsonBuilder builder = new GsonBuilder();
	    return builder.create().toJson(datas);
	 }
	
	  /**
	   * 构造request发票明细
	   * 
	   * @return
	   */
	 private List<Object> buildItems() {
	    List<Object> items = new ArrayList<>();
	    Map<String, Object> data = new HashMap<>();
	    data.put("XMMC", "餐饮");//项目名称
	    data.put("XMSL", 1);//项目数量
	    data.put("SPBM", "1010101020000000000");//商品编码
	    data.put("XMJSHJ", 117);//项目价税合计
	    data.put("SL", 0.17);//税率
	    
	    data.put("GGXH", "");//规格型号
	    data.put("DW", "");//单位
	    data.put("XMDJ", "");//项目单价
	    data.put("XMJE", "");//项目金额
	    data.put("SE", "");//税额
	    data.put("SE", "");//税额
	    data.put("HH", "");//行号
	    	
	    items.add(data);
	    return items;
	 }
	
}
